#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "VObjectTView.h"

#include "Class.h"
#include "VObjectText.h"
#include "TextMarks.h"
#include "Clipper.h"
#include "Picture.h"
#include "PictureItem.h"
#include "ImageItem.h"
#include "Data.h"
#include "Manager.h"

//----- VObjectTextView ---------------------------------------------------

NewMetaImpl0(VObjectTextView,TextView);

VObjectTextView::VObjectTextView(EvtHandler *eh, const Rectangle &r, VObjectText *t,
		bool w, TextViewFlags f, const Point &b, TViewAlign ta, int id)
					: TextView(eh, r, t, w, f, b, ta, id)
{
	if (t->IsKindOf(VObjectText)) {
		t->SetView(this);
		Guard(t, VObjectText)->SetView(this);
		SetFlag(eVObjKbdFocus);
	}
}

VObjectTextView::~VObjectTextView()
{
	if (GetText() != 0 && GetText()->IsKindOf(VObjectText))
		Guard(GetText(), VObjectText)->SetView(0);
}

Command *VObjectTextView::DispatchEvents(Point lp, Token &t, Clipper *vf)
{
	if (t.IsKey() || t.IsFunctionKey() || t.IsCursorKey())
		return TextView::DispatchEvents(lp, t, vf);

	VisualMark *vmp= IsInputForVisualMark(lp);
	if (vmp)
		return vmp->Input(lp, t, vf);
	return TextView::DispatchEvents(lp, t, vf);
}

GrCursor VObjectTextView::GetCursor(Point lp)
{
	if (IsInputForVisualMark(lp))
		return eCrsBoldArrow;
	return TextView::GetCursor(lp);
}

void VObjectTextView::Control(int id, int part, void *val)
{
	if (part == cPartExtentChanged && GetText()->IsKindOf(VObjectText))
		Guard(GetText(), VObjectText)->VObjectChangedSize((VObject*)val);
	else
		TextView::Control(id, part, val);
}

Command *VObjectTextView::PasteData(Data *data)
{
	if (GetText()->IsKindOf(VObjectText)) {
		Object *op;
		if (op= data->AsObject(Meta(Picture)))
			return new PasteVObjectCommand(this, new PictureItem((Picture*)op));

		if (op= data->AsObject(Meta(Bitmap)))
			return new PasteVObjectCommand(this, new ImageItem((Bitmap*)op));

		if (op= data->AsObject(Meta(VObject)))
			return new PasteVObjectCommand(this, (VObject*)op);
	}
	return TextView::PasteData(data);
}

void VObjectTextView::PasteVObject(VObject *vop)
{
	if (!Writeable())
		return;
	if ((!Caret() && !DeleteRequest(start.ch, end.ch)) || !AnySelection())
		return;
	PasteVisualMark(new VObjectMark(vop));
}

void VObjectTextView::PasteVisualMark(VisualMark *vmp)
{
	if (!Writeable())
		return;
	if ((!Caret() && !DeleteRequest(start.ch, end.ch)) || !AnySelection())
		return;
	updateSelection= FALSE;
	Guard(text, VObjectText)->PasteVisualMark(vmp, start.ch, end.ch);
	updateSelection= TRUE;
	PrivSetSelection(start.ch+1, start.ch+1, FALSE);
}

void VObjectTextView::SetOrigin(Point p)
{
	if (p != GetOrigin())
		TextView::SetOrigin(p);
}

IStream& VObjectTextView::ReadFrom(IStream &s)
{
	TextView::ReadFrom(s);
	if (GetText()->IsKindOf(VObjectText))
		((VObjectText*)GetText())->SetView(this);
	return s;
}

Text *VObjectTextView::SetText(Text *newText, bool reveal)
{
	Text *ot= TextView::SetText(newText, reveal);
	if (newText->IsKindOf(VObjectText))
		((VObjectText*)newText)->SetView(this);
	return ot;
}

VisualMark *VObjectTextView::IsInputForVisualMark(Point p)
{
	if (!GetText()->IsKindOf(VObjectText))
		return 0;
	VObjectText *vt= (VObjectText*)GetText();
	int line, ch;
	Point vp;
	PointToPoint(p, &vp, &line, &ch, FALSE);
	VisualMark *vmp= vt->GetVisualMarkAt(ch);
	if (vmp && p.x > vp.x)
		return vmp;
	if (ch > 0) {
		vmp= vt->GetVisualMarkAt(ch-1);
		if (vmp && p.x < vp.x)
			return vmp;
	}
	return 0;
}

//---- PasteVObjectCommand ----------------------------------------------------

NewMetaImpl(PasteVObjectCommand, TextCommand, (TP(vmp), T(newStart), T(newEnd)));

PasteVObjectCommand::PasteVObjectCommand(VObjectTextView *tv, VObject *v,
	int cmdNo, char *cmdName) : TextCommand(tv, cmdNo, cmdName ? cmdName : "Paste Graphic")
{
	vmp= new VObjectMark(v);
	vtv= tv;
	vt= 0;
}

PasteVObjectCommand::PasteVObjectCommand(VObjectTextView *tv, VisualMark *v, int cmdNo,
	char *cmdName) : TextCommand(tv, cmdNo, cmdName ? cmdName : "Paste Graphic")
{
	vmp= v;
	vtv= tv;
	vt= 0;
}

PasteVObjectCommand::~PasteVObjectCommand()
{
}

void PasteVObjectCommand::DoIt()
{
	vtv->PasteVisualMark(vmp);
	tv->GetSelection(&newStart, &newEnd);
	tv->RevealSelection();
}

void PasteVObjectCommand::UndoIt()
{
	tv->SetSelection(oldStart, newEnd, FALSE);
	if (vt == 0) {
		vt= new VObjectText;
		tv->GetText()->Copy(vt, oldStart, newEnd);
	}
	tv->Cut();
	if (oldText->End() != 0)
		tv->Paste(oldText);
	RestoreSelection();
	tv->RevealSelection();
}

void PasteVObjectCommand::RedoIt()
{
	RestoreSelection();
	tv->Paste(vt);
	tv->RevealSelection();
}

//---- StretchWrapperStretcher ------------------------------------------------

class StretchWrapperStretcher: public Command {
	Point newExtent, oldExtent;
	class VObject *vob;
	Rectangle maxRect;
public:
	StretchWrapperStretcher(VObject *v);
	Command *TrackMouse(TrackPhase, Point, Point, Point);
	void TrackFeedback(Point ap, Point pp, bool);
	void TrackConstrain(Point, Point, Point *);
	void DoIt();
	void UndoIt();
};

StretchWrapperStretcher::StretchWrapperStretcher(VObject *v)
						: Command(1212, "Change Extent")
{
	vob= v;
	View *vp= vob->GetView();
	if (vp->IsKindOf(StaticTextView)) {
		StaticTextView *stv= (StaticTextView*)vp;
		maxRect= Rectangle(stv->GetInnerOrigin(), stv->GetInnerExtent());
	} else
		maxRect= vob->GetView()->ContentRect();
}

Command *StretchWrapperStretcher::TrackMouse(TrackPhase tp, Point, Point, Point np)
{
	if (tp == eTrackRelease) {
		newExtent= np - vob->GetOrigin();
		oldExtent= vob->GetExtent();
		if (oldExtent == newExtent)
			return gNoChanges;
		return this;
	}
	return this;
}

void StretchWrapperStretcher::TrackFeedback(Point, Point pp, bool)
{
	GrStrokeRect(NormRect(vob->GetOrigin(), pp));
}

void StretchWrapperStretcher::TrackConstrain(Point, Point, Point *np)
{
	*np= Max(vob->GetOrigin()+ vob->GetMinSize().Extent(), *np);
	*np= NormRect(vob->GetOrigin(), *np).Intersect(maxRect).SE();
}

void StretchWrapperStretcher::DoIt()
{
	vob->SetExtent(newExtent);
}

void StretchWrapperStretcher::UndoIt()
{
	vob->SetExtent(oldExtent);
}

//---- StretchWrapper ----------------------------------------------------------

NewMetaImpl(StretchWrapper,CompositeVObject, (T(interiorOffset), T(extentDiff),
						T(border), TP(interior)));

StretchWrapper::StretchWrapper(VObject *in, const Point &b, int id)
												: CompositeVObject(id, in, 0)
{
	SetFlag(eVObjKbdFocus);
	interior= in;
	if (interior->IsKindOf(View))
		((View*)interior)->SetNextHandler(this);
	border= Max(Point(4,4),b);
}

Metric StretchWrapper::GetMinSize()
{
	interiorOffset= border;
	extentDiff= 2*interiorOffset;
	Metric m= interior->GetMinSize();
	return m.Expand(border);
}

void StretchWrapper::SetOrigin(Point at)
{
	VObject::SetOrigin(at);
	interior->SetOrigin(at+interiorOffset);
}

void StretchWrapper::SetExtent(Point e)
{
	VObject::SetExtent(e);
	interior->SetExtent(e-extentDiff);
}

Command *StretchWrapper::DoLeftButtonDownCommand(Point p, Token, int)
{
	Rectangle r(ContentRect().SE()-gPoint8, gPoint8);

	if (r.ContainsPoint(p))
		return new StretchWrapperStretcher(this);
	return gNoChanges;
}

Command *StretchWrapper::DoKeyCommand(int, Token)
{
	return gNoChanges;
}

void StretchWrapper::Control(int id, int part, void *val)
{
	if (part == cPartExtentChanged && val == interior) {
		if (interior->GetExtent() != GetExtent()+extentDiff) // change from inside
			SetExtent(interior->GetExtent()+extentDiff);
		SetOrigin(GetOrigin());
		VObject::Control(id, part, this);         // change from outside
		return;
	}
	VObject::Control(id, part, val);
}

void StretchWrapper::SendDown(int id, int part, void *val)
{
	if (part == cPartEnableLayoutCntl)
		interior->SetFlag(eVObjLayoutCntl);
	else
		CompositeVObject::SendDown(id, part, val);
}

bool StretchWrapper::KbdFocus(bool in)
{
	bool b= interior->KbdFocus(in);
	ForceRedraw();
	return b;
}

void StretchWrapper::Draw(Rectangle r)
{
	CompositeVObject::Draw(r);
	Manager *m= GetManager();
	if (m && m->GetFirstHandler() == this) {
		GrSetPenNormal();
		GrSetPenInk(ePatGrey50);
		GrSetPenSize(2);
		GrStrokeRect(ContentRect());
		GrPaintRect(Rectangle(ContentRect().SE()-gPoint4, gPoint4), ePatBlack);
	}
}

GrCursor StretchWrapper::GetCursor(Point p)
{
	Rectangle r(ContentRect().SE()-gPoint8, gPoint8);

	if (r.ContainsPoint(p))
		return eCrsMoveStretch;
	return VObject::GetCursor(p);
}

OStream& StretchWrapper::PrintOn(OStream &s)
{
	CompositeVObject::PrintOn(s);
	return s << border SP << interiorOffset SP << extentDiff SP << interior SP;
}

IStream& StretchWrapper::ReadFrom(IStream &s)
{
	CompositeVObject::ReadFrom(s);
	s >> border >> interiorOffset >> extentDiff >> interior;
	if (interior->IsKindOf(View))
		((View*)interior)->SetNextHandler(this);
	return s;
}

